package cn.com.git.example.common.util.security;

import io.jsonwebtoken.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author zhangzhenfu
 * @Description
 * @date 2020/4/3 15:47
 */
public class JwtUtils {

    /**
     * @param ttlMillis token 的有效毫秒数
     * @param claims    创建payload的私有声明
     * @param issuer    签发者
     * @param audience  接受者
     * @param subject   签发内容
     * @param secretKey 生成签名的时候使用的秘钥secret
     * @return
     */
    public static String createToken(long ttlMillis, Map<String, Object> claims, String issuer, String audience, String subject, String secretKey) {

        //生成JWT的时间
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        //下面就是在为payload添加各种标准声明和私有声明了
        //这里其实就是new一个JwtBuilder，设置jwt的body
        JwtBuilder builder = Jwts.builder()
                //如果有私有声明，一定要先设置这个自己创建的私有的声明，这个是给builder的claim赋值，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明的
                .setClaims(claims)
                //设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
                .setId(UUID.randomUUID().toString())
                //代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志。
                .setSubject(subject)
                //接受者的url地址
                .setAudience(audience)
                //签发者的url地址
                .setIssuer(issuer)
                //签发时间
                .setIssuedAt(now)
                //设置签名使用的签名算法和签名使用的秘钥
                .signWith(SignatureAlgorithm.HS256, secretKey);
        //过期时间设置
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            //设置过期时间
            builder.setExpiration(exp);
        }

        return builder.compact();

    }

    /**
     * Token的解密
     *
     * @param token     加密后的token
     * @param secretKey 签名秘钥，和生成的签名的秘钥一模一样
     * @return
     */
    public static TokenEntity parseToken(String token, String secretKey) {
        TokenEntity tokenEntity = null;
        try {
            //得到DefaultJwtParser
            Claims claims = Jwts.parser()
                    //设置签名的秘钥
                    .setSigningKey(secretKey)
                    .parseClaimsJws(token).getBody();
            tokenEntity = new TokenEntity();
            tokenEntity.setAudience(claims.getAudience());
            tokenEntity.setIssuer(claims.getIssuer());
            tokenEntity.setIssuerAt(claims.getIssuedAt());
            tokenEntity.setSubject(claims.getSubject());
            tokenEntity.setExpiration(claims.getExpiration());
            tokenEntity.setClaims(claims);
        } catch (SignatureException | MalformedJwtException e) {
            // jwt 解析错误
            e.printStackTrace();
        } catch (ExpiredJwtException e) {
            // jwt 已经过期，在设置jwt的时候如果设置了过期时间，这里会自动判断jwt是否已经过期，如果过期则会抛出这个异常，我们可以抓住这个异常并作相关处理。
            e.printStackTrace();
        }
        return tokenEntity;
    }

    @Getter
    @Setter
    @ToString
    public static class TokenEntity implements Serializable {
        //有效期
        private Date expiration;
        //签发私有声明
        private Map<String, Object> claims;
        //签发人
        private String issuer;
        //签发时间
        private Date issuerAt;
        //所属人
        private String audience;
        //签发内容
        private String subject;

    }

    public static void main(String[] args) {
        Map<String, Object> claims = new HashMap<>(2);
        claims.put("username", "李三");
        claims.put("password", "888888");
        String token = JwtUtils.createToken(5000, claims, "issuer", "audience", "{name:'咱三'}", "-=4nd2n4f63_235");
        System.out.println(token);
        TokenEntity tokenEntity = JwtUtils.parseToken(token, "-=4nd2n4f63_235");
        System.out.println(tokenEntity);
    }


}
